# Copyright (C) 2006, 2007  Stephane Alnet
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 3
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
# 

#
# For more information visit http://carrierclass.net/
#
use strict; use warnings;

package CCNQ::Proxy::subscriber;
use base qw(CCNQ::Proxy::Base);

sub doc
{
    return <<'HTML';

    A subscriber can be a line or trunk towards one of your downstream
    customers.
    <p>
    Subscribers are uniquely identified by their Username. (You can use
    the Local Numbers screen to assign DIDs to a subscriber.)
    <p>
    There are two methods to authenticate a subscriber when they place a call:
    <ul>
    <li>by the IP address of their endpoint;
    <li>using the Username and Password specified.
    </ul>
    Note that the originating port (UDP or TCP) for the SIP message is never matched;
    the port specified for the Subscriber is only used for calls going to that subscriber
    (see below).
    
    <p>
    If a subscriber places a 7-digits call, the Default_NPA is prepended
    to that 7-digit number to form a 10-digit destination.
    <p>
    You can overwrite the CallerID provided by a subscriber by specifying
    a new value as RPID.
    <p>
    Finally, you must indicate what type of calls this subscriber is
    authorized to place:
    <ul>
    <li>Local calls (on-net calls, or calls to NPANXX identified as "local");
    <li>Premium calls (calls to an NPA or an NXX identified as "premium");
    <li>Long Distance (LD) calls (non-local calls to a North-American number);
    <li>International calls (outside NANPA.com).
    </ul>
    All subscribers can place calls to N11 or *XX numbers.

    <p>
    For calls that must terminate at a subscriber, the following are tried in order:
    <ul>
    <li>the location specified in the registration information, if the subscriber is registered;
    <li>the location (SIP URI) specified as the CFNR (Call Forward on Non-Registered) 
        for the destination Local Number if one is specified (see under Local Numbers);
    <li>as a last resort, the IP address and SIP port number specified for the Subscriber.
    </ul>

HTML
}


sub form
{
    my $self = shift;
    return (
        'Username'              => 'text',
        'Password'              => 'text',
        'IP'                    => 'ip',
        'Port'                  => 'text',
        'SRV'                   => 'text',
        'Default_NPA'           => 'text',
        'RPID'                  => 'text',
        'Account'               => 'text',
        'Allow_Local'           => [1=>'Yes',0=>'No'],
        'Allow_LD'              => [1=>'Yes',0=>'No'],
        'Allow_Premium'         => [1=>'Yes',0=>'No'],
        'Allow_International'   => [1=>'Yes',0=>'No'],
    );
}


sub new_precondition
{
    my $self = shift;
    my %params = @_;

    my $username = $params{username};
    
    warn("No username provided.") if not defined $username or $username eq '';

    my $count = 0;

    $count += $self->run_sql_once(<<'SQL',$username);
        SELECT COUNT(*) FROM subscriber WHERE username = ?
SQL

    $count += $self->run_sql_once(<<'SQL',$username,$self->avp->{src_subs});
        SELECT COUNT(*) FROM avpops WHERE value = ? AND attribute = ?
SQL

    return $count == 0;
}

use Digest::MD5 qw(md5_hex);

sub insert
{
    my $self = shift;
    my %params = @_;
    my $username    = $params{username};
    my $password    = $params{password};
    my $ip          = $params{ip};
    my $port        = $params{port};
    my $srv         = $params{srv};
    my $default_npa = $params{default_npa};
    my $rpid        = $params{rpid};
    my $account     = $params{account};
    my $challenge   = $self->{challenge};

    my $allow_local = $params{allow_local} || 0;
    my $allow_ld    = $params{allow_ld} || 0;
    my $allow_premium = $params{allow_premium} || 0;
    my $allow_intl  = $params{allow_international} || 0;

    $ip = undef unless $ip =~ /^[\d.]+$/;

    my @res;
    if( defined $password and $password ne '' )
    {
        push @res,
            <<'SQL',[$username,$password,md5_hex("$username:$challenge:$password"),md5_hex("$username\@:$challenge:$password")];
            INSERT INTO subscriber(username,domain,password,ha1,ha1b) VALUES (?,'',?,?,?)
SQL
    }
    else
    {
        push @res,
            <<'SQL',[$username];
            INSERT INTO subscriber(username,domain) VALUES (?,'')
SQL
    }

    push @res, <<'SQL',[$account,$username];
        UPDATE subscriber SET account = ? WHERE username = ?
SQL

    # Map IP to username (for IP-based authentication)
    if(defined $ip)
    {
        push @res, $self->_avp_set($ip,'src_subs',$username);
    }

    return (
        @res,
        $self->_avp_set($username,'user_ip',$ip),
        $self->_avp_set($username,'user_port',$port),
        $self->_avp_set($username,'user_srv',$srv),
        $self->_avp_set($username,'default_npa',$default_npa),
        $self->_avp_set($username,'rpid',$rpid),
        $self->_avp_set($username,'allow_local',$allow_local?1:undef),
        $self->_avp_set($username,'allow_ld',$allow_ld?1:undef),
        $self->_avp_set($username,'allow_premium',$allow_premium?1:undef),
        $self->_avp_set($username,'allow_intl',$allow_intl?1:undef),
    );
}

sub delete
{
    my $self = shift;
    my %params = @_;
    my $username = $params{username};
    my $ip       = $params{ip};

    my @res = (
        <<'SQL',[$username],
        DELETE FROM subscriber WHERE username = ? AND domain = ''
SQL
        # Should be redundant with the avp_set(src_subs) below.
        <<'SQL',[$username,$self->avp->{src_subs}],
        DELETE FROM avpops WHERE value = ? AND attribute = ?
SQL

        $self->_avp_set($username,'user_ip',undef),
        $self->_avp_set($username,'user_port',undef),
        $self->_avp_set($username,'default_npa',undef),
        $self->_avp_set($username,'rpid',undef),
        $self->_avp_set($username,'allow_local',undef),
        $self->_avp_set($username,'allow_ld',undef),
        $self->_avp_set($username,'allow_premium',undef),
        $self->_avp_set($username,'allow_intl',undef),
    );

    if(defined $ip)
    {
        push @res, $self->_avp_set($ip,'src_subs',undef);
    }

    return @res;
}

sub list
{
    my $self = shift;
    my @params = $self->vars;
    my %params = @params;
    
    my @where = ();
    my @where_values = ();
    my $account = $params{account};
    push( @where, '"Account" = ?' ), push( @where_values, $account )
        if defined $account and $account ne '';

    my $where = '';
    $where = 'WHERE '. join('AND', map { "($_)" } @where ) if @where;

    return (<<SQL,[$self->avp->{src_subs},$self->avp->{user_port},$self->avp->{user_srv},$self->avp->{rpid},$self->avp->{default_npa},$self->avp->{allow_local},$self->avp->{allow_ld},$self->avp->{allow_premium},$self->avp->{allow_intl},$self->avp->{src_subs},@where_values],undef);
        SELECT username AS Username,
               (SELECT account  FROM subscriber WHERE username = main.username ) AS "Account",
               (SELECT password FROM subscriber WHERE username = main.username ) AS Password,
               (SELECT uuid FROM avpops WHERE value = main.username AND attribute = ?) AS "IP",
               (SELECT value FROM avpops WHERE uuid = main.username AND attribute = ?) AS "Port",
               (SELECT value FROM avpops WHERE uuid = main.username AND attribute = ?) AS "SRV",
               (SELECT value FROM avpops WHERE uuid = main.username AND attribute = ?) AS "RPID",
               (SELECT value FROM avpops WHERE uuid = main.username AND attribute = ?) AS "Default_NPA",
               (SELECT COUNT(value) FROM avpops WHERE uuid = main.username AND attribute = ?) AS "Allow_Local",
               (SELECT COUNT(value) FROM avpops WHERE uuid = main.username AND attribute = ?) AS "Allow_LD",
               (SELECT COUNT(value) FROM avpops WHERE uuid = main.username AND attribute = ?) AS "Allow_Premium",
               (SELECT COUNT(value) FROM avpops WHERE uuid = main.username AND attribute = ?) AS "Allow_International",
               (SELECT contact FROM location WHERE username = main.username) AS "Contact",
               (SELECT received FROM location WHERE username = main.username) AS "Received",
               (SELECT user_agent FROM location WHERE username = main.username) AS "User_Agent",
               (SELECT expires FROM location WHERE username = main.username) AS "Expires"
        FROM 
        (
            SELECT username AS username FROM subscriber main
            UNION
            SELECT value AS username FROM avpops WHERE attribute = ?
        ) main
        $where
        ORDER BY username ASC
SQL
}

1;